package cn.tedu.charging.order.consumer;

import cn.tedu.charging.common.constant.AmqpComponentConst;
import cn.tedu.charging.common.pojo.message.DelayCheckMessage;
import cn.tedu.charging.order.serialize.MessageSerializer;
import cn.tedu.charging.order.service.ConsumerService;
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

@Component
@Slf4j
public class AmqpConsumer {
    @Autowired
    private MessageSerializer<DelayCheckMessage> serializer;
    @Autowired
    private ConsumerService consumerService;
    @Autowired
    private RedisTemplate redisTemplate;
    @RabbitListener(queues = AmqpComponentConst.DELAY_BILL_DL_QUEUE)
    public void receive(String message,Message msg, Channel channel){
        //消费入口 解析消息
        log.info("收到消息:{}",message);
        DelayCheckMessage delayMsg = serializer.deserialize(message, DelayCheckMessage.class);
        //1.实现抢锁逻辑
        //1.1抢锁 setnx string数据结构 从redisTemplate中获取操作对象
        ValueOperations valueOps = redisTemplate.opsForValue();
        //1.2抢锁 key值和共享业务数据有关 orderNo
        String lockKey="charging:order:consume:"+delayMsg.getOrderNo();
        //1.3执行抢锁
        Boolean haveLock = false;
        Integer count=0;
        try{
            do{
                //do做了,就说明要抢锁了,count++计数
                count++;
                //只允许最多抢三次,如果count++之后已经等于4就说明已经抢了三次了 如果count!=1 说明是多次抢锁了
                if (count>1&&count<4){
                    //说明多次抢锁没达到上限 沉睡100在抢
                    Thread.sleep(100);
                }else if (count>=4){
                    //超过抢锁上限,别在抢了,业务也不执行了
                    log.error("超过抢锁上限,别在抢了,业务也不执行了,orderNo:{}",delayMsg.getOrderNo());
                    return;
                }
                haveLock = valueOps.setIfAbsent(lockKey, "", 5, TimeUnit.SECONDS);
            }while (!haveLock);
            //抢到锁,当前线程有资格执行业务
            consumerService.handleDelayCheck(delayMsg);
        }catch(Exception e){
            log.error("消费失败,手动确认失败",e);
        }finally{
            //释放锁
            redisTemplate.delete(lockKey);
        }
        try{
            channel.basicAck(msg.getMessageProperties().getDeliveryTag(),false);
            log.debug("消费成功,手动确认");
        }catch (Exception e){
            log.error("消费失败,手动确认失败",e);
        }
    }
}
